{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "lSTHo_BAn_FZ"
      },
      "source": [
        "# PyTurbo-Aero 2D Design Tutorial\n",
        "In this tutorial you will learn how to construct a 2D Airfoil beginning with a camberline. The pressure and suction sides are then defined from the camberline by spacing control points perpendicularly.\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "dUeUZN-Sn_Fh"
      },
      "source": [
        "## Building a camberline\n",
        "A camberline in an airfoil is the line separating the pressure side from the suction side. The reason why a side is called pressure and suction is due to the velocity of the flow moving around each side. Suction side has some concavity to it like hill or mountain. This causes the velocity of the gasses to accelerate as it passes around the suction side. Lower velocity means lower static pressure, hence suction. The pressure side has the opposite effect. The gasses passing over the pressure side move slower and this causes the static pressure to rise. The difference in pressure creates a lift force. In turbomachinery, this lift force is used to rotate the turbine or compressor.\n",
        "\n",
        "Example of a turbomachinery airfoil:\n",
        "![Airfoil](https://scholar.lib.vt.edu/ejournals/JOTS/v37/v37n1/images/lowe1.jpeg)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "hx8tXQyxn_Fj"
      },
      "source": [
        "## Installing PyTurbo-Aero\n",
        "Run the code below to pip install the latest version of pyturbo-aero"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "7vNfz8Qen_Fk",
        "outputId": "8007e488-81b3-48c4-c54a-16003514c54d"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Requirement already satisfied: pyturbo-aero in /opt/homebrew/Caskroom/miniconda/base/envs/dev/lib/python3.10/site-packages (1.0.14)\n",
            "Requirement already satisfied: matplotlib in /opt/homebrew/Caskroom/miniconda/base/envs/dev/lib/python3.10/site-packages (from pyturbo-aero) (3.9.2)\n",
            "Requirement already satisfied: numpy in /opt/homebrew/Caskroom/miniconda/base/envs/dev/lib/python3.10/site-packages (from pyturbo-aero) (2.0.2)\n",
            "Requirement already satisfied: numpy-stl in /opt/homebrew/Caskroom/miniconda/base/envs/dev/lib/python3.10/site-packages (from pyturbo-aero) (3.1.2)\n",
            "Requirement already satisfied: pandas in /opt/homebrew/Caskroom/miniconda/base/envs/dev/lib/python3.10/site-packages (from pyturbo-aero) (2.2.2)\n",
            "Requirement already satisfied: plotly in /opt/homebrew/Caskroom/miniconda/base/envs/dev/lib/python3.10/site-packages (from pyturbo-aero) (5.24.1)\n",
            "Requirement already satisfied: scipy in /opt/homebrew/Caskroom/miniconda/base/envs/dev/lib/python3.10/site-packages (from pyturbo-aero) (1.13.1)\n",
            "Requirement already satisfied: tqdm in /opt/homebrew/Caskroom/miniconda/base/envs/dev/lib/python3.10/site-packages (from pyturbo-aero) (4.66.5)\n",
            "Requirement already satisfied: contourpy>=1.0.1 in /opt/homebrew/Caskroom/miniconda/base/envs/dev/lib/python3.10/site-packages (from matplotlib->pyturbo-aero) (1.3.0)\n",
            "Requirement already satisfied: cycler>=0.10 in /opt/homebrew/Caskroom/miniconda/base/envs/dev/lib/python3.10/site-packages (from matplotlib->pyturbo-aero) (0.12.1)\n",
            "Requirement already satisfied: fonttools>=4.22.0 in /opt/homebrew/Caskroom/miniconda/base/envs/dev/lib/python3.10/site-packages (from matplotlib->pyturbo-aero) (4.53.1)\n",
            "Requirement already satisfied: kiwisolver>=1.3.1 in /opt/homebrew/Caskroom/miniconda/base/envs/dev/lib/python3.10/site-packages (from matplotlib->pyturbo-aero) (1.4.7)\n",
            "Requirement already satisfied: packaging>=20.0 in /opt/homebrew/Caskroom/miniconda/base/envs/dev/lib/python3.10/site-packages (from matplotlib->pyturbo-aero) (24.1)\n",
            "Requirement already satisfied: pillow>=8 in /opt/homebrew/Caskroom/miniconda/base/envs/dev/lib/python3.10/site-packages (from matplotlib->pyturbo-aero) (10.4.0)\n",
            "Requirement already satisfied: pyparsing>=2.3.1 in /opt/homebrew/Caskroom/miniconda/base/envs/dev/lib/python3.10/site-packages (from matplotlib->pyturbo-aero) (3.1.4)\n",
            "Requirement already satisfied: python-dateutil>=2.7 in /opt/homebrew/Caskroom/miniconda/base/envs/dev/lib/python3.10/site-packages (from matplotlib->pyturbo-aero) (2.9.0)\n",
            "Requirement already satisfied: python-utils>=3.4.5 in /opt/homebrew/Caskroom/miniconda/base/envs/dev/lib/python3.10/site-packages (from numpy-stl->pyturbo-aero) (3.8.2)\n",
            "Requirement already satisfied: pytz>=2020.1 in /opt/homebrew/Caskroom/miniconda/base/envs/dev/lib/python3.10/site-packages (from pandas->pyturbo-aero) (2024.2)\n",
            "Requirement already satisfied: tzdata>=2022.7 in /opt/homebrew/Caskroom/miniconda/base/envs/dev/lib/python3.10/site-packages (from pandas->pyturbo-aero) (2024.1)\n",
            "Requirement already satisfied: tenacity>=6.2.0 in /opt/homebrew/Caskroom/miniconda/base/envs/dev/lib/python3.10/site-packages (from plotly->pyturbo-aero) (9.0.0)\n",
            "Requirement already satisfied: six>=1.5 in /opt/homebrew/Caskroom/miniconda/base/envs/dev/lib/python3.10/site-packages (from python-dateutil>=2.7->matplotlib->pyturbo-aero) (1.16.0)\n",
            "Requirement already satisfied: typing-extensions>3.10.0.2 in /opt/homebrew/Caskroom/miniconda/base/envs/dev/lib/python3.10/site-packages (from python-utils>=3.4.5->numpy-stl->pyturbo-aero) (4.12.2)\n"
          ]
        }
      ],
      "source": [
        "!pip install pyturbo-aero"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 370
        },
        "id": "Oj1rQ57gn_Fn",
        "outputId": "f57c0d98-ed23-4ac8-a3f8-f6813a6665ff"
      },
      "outputs": [],
      "source": [
        "# Construction of camber line\n",
        "import sys\n",
        "import numpy as np\n",
        "from pyturbo.aero import Airfoil2D\n",
        "from pyturbo.helper import exp_ratio\n",
        "\n",
        "stator_hub = Airfoil2D(alpha1=0,alpha2=72,axial_chord=0.038,stagger=58) # This creates the camberline\n",
        "stator_hub.plot_camber()\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "NVtEbvJQn_Fp"
      },
      "source": [
        "## Building Suction and Pressure side and the Leading edge\n",
        "Leading edge is easily constructed using a single line of code. The pressure side is built by providing control point heights from the camber line. 1.2 refers to the expansion ratio. You can see an example of what the expansion ratio does by calling exp_ratio function below.\n",
        "Try changing some parameters to see what kind of geometries you can get."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 4,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "nzR5FYejn_Fq",
        "outputId": "f0356cef-fb71-4a28-9ad7-cf48bb108e58"
      },
      "outputs": [
        {
          "ename": "NameError",
          "evalue": "name 'exp_ratio' is not defined",
          "output_type": "error",
          "traceback": [
            "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
            "\u001b[0;31mNameError\u001b[0m                                 Traceback (most recent call last)",
            "Cell \u001b[0;32mIn[4], line 2\u001b[0m\n\u001b[1;32m      1\u001b[0m \u001b[38;5;66;03m# Expansion ratio\u001b[39;00m\n\u001b[0;32m----> 2\u001b[0m x \u001b[38;5;241m=\u001b[39m \u001b[43mexp_ratio\u001b[49m(\u001b[38;5;241m1.2\u001b[39m,\u001b[38;5;241m5\u001b[39m)\n\u001b[1;32m      3\u001b[0m dx \u001b[38;5;241m=\u001b[39m np\u001b[38;5;241m.\u001b[39mdiff(x)\n\u001b[1;32m      4\u001b[0m \u001b[38;5;28mprint\u001b[39m(x) \u001b[38;5;66;03m# position\u001b[39;00m\n",
            "\u001b[0;31mNameError\u001b[0m: name 'exp_ratio' is not defined"
          ]
        }
      ],
      "source": [
        "# Expansion ratio\n",
        "x = exp_ratio(1.2,5)\n",
        "dx = np.diff(x)\n",
        "print(x) # position\n",
        "print(dx) # difference\n",
        "print(dx[1]/dx[0]) # this is the expansion ratio"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 308
        },
        "id": "PqkJYilGn_Fr",
        "outputId": "d94acd42-6a6f-4785-90bf-418d0f43f6d5"
      },
      "outputs": [],
      "source": [
        "# Building Leading Edge\n",
        "stator_hub.le_thickness_add(0.04)\n",
        "# Building the Pressure side\n",
        "ps_height = [0.0500,0.0200,-0.0100] # These are thicknesses\n",
        "stator_hub.ps_thickness_add(thicknessArray=ps_height,expansion_ratio=1.2)\n",
        "\n",
        "ss_height=[0.2400, 0.2000, 0.1600, 0.1400]\n",
        "stator_hub.ss_thickness_add(thicknessArray=ss_height,camberPercent=0.8,expansion_ratio=1.2)\n",
        "stator_hub.le_thickness_match()\n",
        "stator_hub.te_create(radius=0.001,wedge_ss=2.5,wedge_ps=2.4)\n",
        "\n",
        "stator_hub.flow_guidance2(10)\n",
        "stator_hub.plot2D()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "FYa5-ta9n_F3"
      },
      "source": [
        "### Bonus: Pitch to Chord Ratio\n",
        "Pitch to chord refers to ratio of gap between the blades relative to their length. Smaller the gap \\(lower s/c ratio\\) will keep the airflow passing between the blades from separating which can help blades that have a lot of turning, but this leads to increase weight and results in less massflow. It is ideal to keep this ratio between design limits for example 0.7 to 0.95 for stators and 0.7 and 0.85 for rotors.\n",
        "\n",
        "Visualizing the spacing of your design by specifying a pitch to chord ratio. </br>\n",
        "Example:</br>\n",
        "<img src=\"https://github.com/nasa/pyturbo-aero/blob/main/tutorials/_static/pitch_to_chord.jpg?raw=1\" width=\"500px\">"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "MfwOfbr-n_F4",
        "outputId": "38000751-0e0f-427d-ec8e-f970432a75a9"
      },
      "outputs": [],
      "source": [
        "stator_hub.plot2D_channel(0.75)\n",
        "# The lines connecting the blade show the minimum distance at the inlet and outlet. The outlet distance is called the throat area.\n",
        "# If the pressure ratio between upstream total and downstream static (P0/P) is > 1.89 then you can use the choke flow equation to compute the massflow at the throat (M = 1)\n",
        "# https://www.grc.nasa.gov/www/k-12/airplane/mflchk.html"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "xamRIA6An_F5"
      },
      "source": [
        "## Plotting the 2nd derivative\n",
        "Even with a favorable pressure gradient the flow can separate if there is too much turning. The second derivative can be an indicator of separation. You can think of biking or driving over a speed bump. Going too fast you can get air. This is sort of what happens in a Turbine and Compressor. Air can separate from the surface causing a separation bubble with a recirculation region. We want to minimize this in design.\n",
        "\n",
        "The points along the suction and pressure side are not spaced equally interms of x or y but they do maintain constant distance between the points. This algorithm was used to compute the second derivative https://mathformeremortals.wordpress.com/2013/01/12/a-numerical-second-derivative-from-three-points/"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "dh81Yjnun_F6",
        "outputId": "d128e4d8-91ac-4fb2-e244-14b628087798"
      },
      "outputs": [],
      "source": [
        "stator_hub.plot_derivative2()"
      ]
    }
  ],
  "metadata": {
    "colab": {
      "provenance": []
    },
    "kernelspec": {
      "display_name": "Python 3.10.6 64-bit",
      "language": "python",
      "name": "python3"
    },
    "language_info": {
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3.10.14"
    },
    "orig_nbformat": 4,
    "vscode": {
      "interpreter": {
        "hash": "b0fa6594d8f4cbf19f97940f81e996739fb7646882a419484c72d19e05852a7e"
      }
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}
